﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenTK;

namespace GLPong
{
    public interface IDrawable
    {
        void Display();
    }

    public interface IPhysical
    {
        void UpdatePos(long delta);
    }

    public interface IPhysicalDrawable : IDrawable, IPhysical { }

    class ObjectManager
    {
        private static ObjectManager _instance = null;
        public static ObjectManager Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new ObjectManager();

                return _instance;
            }
        }

        private Dictionary<Guid, IPhysicalDrawable> _objectList = new Dictionary<Guid, IPhysicalDrawable>();

        Palette _p1;
        Palette _p2;
        Ball _ball;

        long _lastTicks = DateTime.Now.Ticks;

        float _gameSetWidth;
        float _gameSetHeight;

        ObjectManager()
        {
        }

        public void DisplayObjects()
        {
            foreach (IDrawable drawable in _objectList.Values)
            {
                drawable.Display();
            }
        }

        public void CreateGameSet()
        {
            _gameSetWidth = 100;
            _gameSetHeight = 100;

            float ballRadius = 2;

            _p1 = new Palette((_gameSetWidth - ballRadius) / 2, 0, 2, 20, System.Drawing.Color.FromArgb(0, 128, 0));
            _p2 = new Palette((-_gameSetWidth + ballRadius) / 2, 0, 2, 20, System.Drawing.Color.FromArgb(0, 128, 0));
            _ball = new Ball(0, 0, ballRadius, System.Drawing.Color.FromArgb(128, 128, 0));

            _objectList.Add(Guid.NewGuid(), _p1);
            _objectList.Add(Guid.NewGuid(), _p2);
            _objectList.Add(Guid.NewGuid(), _ball);

            InputManager.Instance.Init(_p1, _p2);
        }

        public void UpdateMechanics()
        {
            long nowTicks = DateTime.Now.Ticks;
            foreach (IPhysical obj in _objectList.Values)
                obj.UpdatePos(nowTicks - _lastTicks);
            _lastTicks = nowTicks;

            PaletteBounce(_p1);
            PaletteBounce(_p2);

            if ((_ball.X + _ball.Radius > _gameSetWidth / 2) ||
                (_ball.X - _ball.Radius < -_gameSetWidth / 2))
            {
                Bounce(BounceType.BounceX);
                _ball.X = Math.Sign(_ball.X) * (_gameSetWidth / 2 - _ball.Radius);
            }

            if ((_ball.Y + _ball.Radius > _gameSetHeight / 2) ||
                (_ball.Y - _ball.Radius < -_gameSetHeight / 2))
            {
                Bounce(BounceType.BounceY);
                _ball.Y = Math.Sign(_ball.Y) * (_gameSetHeight / 2 - _ball.Radius);
            }
        }

        private void PaletteBounce(Palette p)
        {
            if ((Math.Abs(_ball.X - p.X) <= p.W / 2 + _ball.Radius) &&
                Math.Abs(_ball.Y - p.Y) <= p.H / 2 + _ball.Radius)
            {
                Bounce(BounceType.BounceX);
                _ball.X = _ball.X < 0 ? p.X + p.W / 2 + _ball.Radius : p.X - p.W / 2 - _ball.Radius;
            }
        }

        enum BounceType { BounceX = 0, BounceY = 1 }

        private void Bounce(BounceType type)
        {
            Vector3d newSpeed = _ball.Speed;
            if (type == BounceType.BounceX)
                newSpeed.X = -_ball.Speed.X;
            else if (type == BounceType.BounceY)
                newSpeed.Y = -_ball.Speed.Y;
            else
                throw new Exception("ERROR : Wrong BounceType, can only bounce in X or Y");

            _ball.Speed = newSpeed;
        }
    }
}
